﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using ifunction.Twilio;
using Newtonsoft.Json;

namespace ifunction.TwilioApi
{
    /// <summary>
    /// Class TwilioClient.
    /// </summary>
    public class TwilioClient
    {
        protected const string baseUrl = "https://api.twilio.com/2010-04-01";

        #region Property

        /// <summary>
        /// Gets or sets the account sid.
        /// </summary>
        /// <value>The account sid.</value>
        public string AccountSid
        {
            get;
            protected set;
        }

        /// <summary>
        /// Gets or sets the token.
        /// </summary>
        /// <value>The token.</value>
        public string Token
        {
            get;
            protected set;
        }

        #endregion

        #region Constructor

        public TwilioClient(string accountSid, string token)
        {
            this.AccountSid = accountSid;
            this.Token = token;
        }

        #endregion

        #region Message

        /// <summary>
        /// Sends the MMS message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="callback">The callback.</param>
        public void SendMessage(Message message, Action<Message> callback)
        {
            if (message != null)
            {
                var request = CreateSendMessageRequest(message.FromNumber, message.ToNumber, message.Body, message.MediaUri == null ? null : message.MediaUri.ToString());

                HandleResponse(request, callback, new Action<Message>((Message callbackMessage) =>
                {
                    if (callbackMessage != null)
                    {
                        callbackMessage.Key = message.Key;
                        callbackMessage.Status = MessagingStatus.Queued;
                    }
                }));
            }
        }

        /// <summary>
        /// Sends the message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns>Message.</returns>
        /// <exception cref="System.InvalidOperationException">SendMessage</exception>
        public Message SendMessage(Message message)
        {
            try
            {
                message.CheckNullObject("message");

                var request = CreateSendMessageRequest(message.FromNumber, message.ToNumber, message.Body, message.MediaUri == null ? null : message.MediaUri.ToString());

                return HandleResponse<Message>(request);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("SendMessage", ex);
            }
        }

        /// <summary>
        /// Creates the send SMS message request.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="to">The automatic.</param>
        /// <param name="body">The body.</param>
        /// <returns>HttpWebRequest.</returns>
        protected HttpWebRequest CreateSendMessageRequest(string from, string to, string body, string mediaUrl = null)
        {
            const string urlFormat = "/Accounts/{0}/Messages.json";

            var request = this.CreateRequest(string.Format(urlFormat, this.AccountSid), "POST");

            Dictionary<string, string> postData = new Dictionary<string, string>();
            postData.Add("From", from.GetStringValue().ToUrlEncodedText());
            postData.Add("To", to.GetStringValue().ToUrlEncodedText());
            postData.Add("Body", body.GetStringValue().ToUrlEncodedText());

            if (!string.IsNullOrWhiteSpace(mediaUrl))
            {
                postData.Add("MediaUrl", mediaUrl);
            }

            request.FillData("POST", postData);

            return request;
        }

        /// <summary>
        /// Gets the message log.
        /// </summary>
        /// <param name="messageSid">The message sid.</param>
        /// <returns>Message.</returns>
        public Message GetMessageLog(string messageSid)
        {
            if (!string.IsNullOrWhiteSpace(messageSid))
            {
                var request = CreateMessageLogRequest(messageSid);
                return HandleResponse<Message>(request);
            }

            return null;
        }

        /// <summary>
        /// Creates the message log request.
        /// </summary>
        /// <param name="sid">The sid.</param>
        /// <returns>HttpWebRequest.</returns>
        protected HttpWebRequest CreateMessageLogRequest(string sid)
        {
            const string urlFormat = "/Accounts/{0}/Messages/{1}.json";

            var request = this.CreateRequest(string.Format(urlFormat, this.AccountSid, sid), "GET");

            return request;
        }

        #endregion

        #region Http

        /// <summary>
        /// Creates the request.
        /// </summary>
        /// <param name="uri">The URI.</param>
        /// <param name="methodType">Type of the method.</param>
        /// <returns>HttpWebRequest.</returns>
        protected HttpWebRequest CreateRequest(Uri uri, string methodType = null)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);

            if (string.IsNullOrWhiteSpace(methodType))
            {
                methodType = "GET";
            }

            request.Method = methodType;

            NetworkCredential credential = new NetworkCredential(this.AccountSid, this.Token);
            request.Credentials = credential;

            return request;
        }

        /// <summary>
        /// Creates the request.
        /// </summary>
        /// <param name="relevantUrl">The relevant URL.</param>
        /// <param name="methodType">Type of the method.</param>
        /// <returns>HttpWebRequest.</returns>
        protected HttpWebRequest CreateRequest(string relevantUrl, string methodType = null)
        {
            return this.CreateRequest(new Uri(baseUrl + relevantUrl.GetStringValue()), methodType);
        }

        /// <summary>
        /// Handles the response.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request">The request.</param>
        /// <param name="callback">The callback.</param>
        /// <exception cref="System.Exception">HandleResponse</exception>
        protected void HandleResponse<T>(HttpWebRequest request, Action<T> callback, Action<T> dataAdaptDelegate = null)
        {
            WebResponse response = null;

            if (request != null)
            {
                try
                {
                    response = request.GetResponse();
                    var responseContent = response.ReadAsText(Encoding.UTF8);

                    if (callback != null)
                    {
                        var callbackObject = JsonConvert.DeserializeObject<T>(responseContent);

                        if (callbackObject != null)
                        {
                            if (dataAdaptDelegate != null)
                            {
                                dataAdaptDelegate(callbackObject);
                            }

                            callback(callbackObject);
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("HandleResponse", ex);
                }
                finally
                {
                    if (response != null)
                    {
                        response.Close();
                    }
                }
            }
        }

        /// <summary>
        /// Handles the response.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request">The request.</param>
        /// <returns>``0.</returns>
        /// <exception cref="Exception">HandleResponse</exception>
        protected T HandleResponse<T>(HttpWebRequest request)
        {
            WebResponse response = null;

            if (request != null)
            {
                try
                {
                    response = request.GetResponse();
                    var responseContent = response.ReadAsText(Encoding.UTF8);
                    return JsonConvert.DeserializeObject<T>(responseContent);
                }
                catch (Exception ex)
                {
                    throw new Exception("HandleResponse", ex);
                }
                finally
                {
                    if (response != null)
                    {
                        response.Close();
                    }
                }
            }

            return default(T);
        }

        #endregion
    }
}
